﻿namespace LaoLuo
{
    using System;

    /// <summary>
    /// 精确的时间分析器
    /// </summary>
    public static class DateTimeParser
    {
        /// <summary>
        /// 默认的时间格式
        /// </summary>
        private static readonly string[] defaultDateFormats = { "yyyyMMdd", "yyMMdd", "yyyy", "yy", "MM-dd HH:mm", "HH:mm", "yyyyMMddHHmmss", "dd日 HH:mm" };

        /// <summary>
        /// 获取默认的时间格式
        /// </summary>
        public static string[] DefaultDateFormats
        {
            get { return defaultDateFormats; }
        }

        /// <summary>
        /// 尝试从字符串获取时间
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns>如果成功获取时间则返回时间,否则返回null</returns>
        public static DateTime? TryGetDateFromString(string str)
        {
            return TryGetDateFromString(str, defaultDateFormats);
        }

        /// <summary>
        /// 尝试从字符串获取时间
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="dateFormats">时间格式</param>
        /// <returns>如果成功获取时间则返回时间,否则返回null</returns>
        public static DateTime? TryGetDateFromString(string str, params string[] dateFormats)
        {
            DateTime ret;

            if (DateTime.TryParse(str, out ret))
            {
                return ret;
            }

            if (DateTime.TryParseExact(str, dateFormats, null, System.Globalization.DateTimeStyles.AllowWhiteSpaces, out ret))
            {
                return ret;
            }

            return null;
        }

        /// <summary>
        /// 尝试从字符串获取时间,会尝试截取尾部
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="truncated">截取的部分</param>
        /// <param name="datePart">日期的部分</param>
        /// <returns>如果成功获取时间则返回时间,否则返回null</returns>
        public static DateTime? TryGetDateFromStringWithTruncateTail(string str, out string truncated, out string datePart)
        {
            return TryGetDateFromStringWithTruncateTail(str, defaultDateFormats, (DateTime d) => d.Year > 1970 && d.Year <= DateTime.Now.Year, out truncated, out datePart);
        }

        /// <summary>
        /// 尝试从字符串获取时间,会尝试截取尾部
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="isValidDate">检验是否是有效的时间的方法</param>
        /// <param name="truncated">截取的部分</param>
        /// <param name="datePart">日期的部分</param>
        /// <returns>如果成功获取时间则返回时间,否则返回null</returns>
        public static DateTime? TryGetDateFromStringWithTruncateTail(string str, Func<DateTime, bool> isValidDate, out string truncated, out string datePart)
        {
            return TryGetDateFromStringWithTruncateTail(str, defaultDateFormats, isValidDate, out truncated, out datePart);
        }

        /// <summary>
        /// 尝试从字符串获取时间,会尝试截取尾部
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="dateFormats">时间格式</param>
        /// <param name="isValidDate">检验是否是有效的时间的方法</param>
        /// <param name="truncated">截取的部分</param>
        /// <param name="datePart">日期的部分</param>
        /// <returns>如果成功获取时间则返回时间,否则返回null</returns>
        public static DateTime? TryGetDateFromStringWithTruncateTail(string str, string[] dateFormats, Func<DateTime, bool> isValidDate, out string truncated, out string datePart)
        {
            DateTime? r;
            var strBak = str;
            var truncateTime = 0;
            datePart = string.Empty;
            do
            {
                r = TryGetDateFromString(str, dateFormats);
                if (r.HasValue)
                {
                    if (isValidDate(r.Value))
                    {
                        datePart = str;
                        break;
                    }
                    else
                    {
                        r = null;
                    }
                }

                str = str.Substring(0, str.Length - 1);
                truncateTime++;
            }
            while (!r.HasValue && str.Length > 1);
            truncated = strBak.Substring(strBak.Length - truncateTime);
            return r;
        }
    }
}
